package com.gbase8c.dmt.db.oracle;

import com.gbase8c.dmt.db.object.ConstraintObject;
import com.gbase8c.dmt.db.object.TableObject;

import com.gbase8c.dmt.model.migration.dto.ConstraintDto;
import com.gbase8c.dmt.model.migration.dto.DataSourceDto;
import com.gbase8c.dmt.model.migration.dto.TableDto;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class ConstraintObjectImpl extends MetaImpl implements ConstraintObject {

    protected TableObject tableObject;

    private static final Map<String, String> srcConsTypeToConsType = Maps.newHashMap();

    static {
        srcConsTypeToConsType.put("C",CHECK);
        srcConsTypeToConsType.put("P",PK);
        srcConsTypeToConsType.put("U",UNIQUE);
        srcConsTypeToConsType.put("R",FK);
    }

    public ConstraintObjectImpl(DataSourceDto dataSourceDto) {
        super(dataSourceDto);
        this.tableObject = new TableObjectImpl(dataSourceDto);
    }

    @Override
    public List<String> getNames(Map<String, Object> params) {
        return null;
    }

    @Override
    public ConstraintDto get(String name, Map<String, Object> params) {
        return null;
    }

    @Override
    public ConstraintDto convert(ConstraintDto constraintDto, Map<String, Object> params) {
        return null;
    }

    @Override
    public String sql(ConstraintDto constraintDto, Map<String, Object> params) {
        return null;
    }

    @Override
    public List<ConstraintDto> getConstraintDtos(String schema) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("schema", schema);
        List<String> tableNameList = tableObject.getNames(params);

        List<List<String>> tableLists = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(tableNameList)){
            tableLists = split(tableNameList,1000);
        }

        String tableNames = null;
        List<ConstraintDto>  constraintDtos = Lists.newArrayList();
        StringBuilder sql = new StringBuilder("select constraint_name as name,constraint_type as srcType from dba_constraints where owner = ? ");
        if (CollectionUtils.isNotEmpty(tableLists)){
            if (tableLists.size() == 1){
                tableNames = tableLists.get(0).stream().map(s->"\'" +s + "\'").collect(Collectors.joining(","));
                sql.append("and table_name in (" + tableNames + ")");
            } else if (tableLists.size() > 1){
                String tableNames1 = tableLists.get(0).stream().map(s->"\'" +s + "\'").collect(Collectors.joining(","));
                sql.append("and table_name in (" + tableNames1 + ")");
                for (int i = 1; i < tableLists.size(); i++){
                    String tableNamesi = tableLists.get(i).stream().map(s->"\'" +s + "\'").collect(Collectors.joining(","));
                    sql.append("or table_name in (" + tableNamesi + ")");
                }
            }
        }

//        if(CollectionUtils.isNotEmpty(tableNameList)){
//            tableNames = tableNameList.stream().map(s->"\'" +s + "\'").collect(Collectors.joining(","));
//            sql.append("and table_name in (" + tableNames + ")");
//        }
        constraintDtos = query(sql.toString(), new BeanListHandler<>(ConstraintDto.class), schema);
        return constraintDtos;
    }

    @Override
    public List<ConstraintDto> getConstraintDtos(String schema, String table) {
        String sql = "select constraint_name as name,constraint_type as srcType from dba_constraints where owner = ? and table_name = ?";
        List<ConstraintDto> constraintDtos = query(sql, new BeanListHandler<>(ConstraintDto.class), schema, table);
        for (ConstraintDto dto : constraintDtos) {
            dto.setConsType(srcConsTypeToConsType.get(dto.getSrcType()));
        }
        return constraintDtos;
    }

    @Override
    public ConstraintDto getConstraintDto(String schema, ConstraintDto constraintDto) {
        String sql = "select owner as schema,table_name as tableName,constraint_name as name,constraint_type as srcType from dba_constraints where owner = ? and constraint_name = ?";
        String getConditionSql = "select search_condition from dba_constraints where owner = ? and constraint_name = ?";
        String columnSql = "select column_name from dba_cons_columns cl where cl.owner = ? AND cl.table_name = ? AND cl.constraint_name = ? ";
        String getReferencedConsSql = "SELECT R_CONSTRAINT_NAME FROM DBA_CONSTRAINTS WHERE owner = ? and constraint_type = 'R' and table_name = ? and constraint_name = ? ";
        String getReferencedTableSql = "SELECT table_name FROM DBA_CONSTRAINTS WHERE owner = ? AND CONSTRAINT_name = ? ";
        ConstraintDto dto = null;
        ConstraintDto referencedDto = null;
        List<String> columns = Lists.newArrayList();
        //获得约束基本信息:模式、表名、约束名、约束类型
        dto = query(sql, new BeanHandler<>(ConstraintDto.class), schema,constraintDto.getName());
        //当check约束时,会有描述语句 :column is not null
        List<String> condition = query(getConditionSql,new ColumnListHandler<>(),schema,constraintDto.getName());
        //约束所涉及到的列
        columns = query(columnSql,new ColumnListHandler<>(),schema,dto.getTableName(),dto.getName());
        //构造ColumnDto
        List<TableDto.ColumnDto> columnDtos = Lists.newArrayList();
        List<TableDto.ColumnDto> referencedColumnDto = Lists.newArrayList();
        for (String column:columns) {
            TableDto.ColumnDto columnDto = TableDto.ColumnDto.builder()
                    .schema(schema)
                    .tableName(dto.getTableName())
                    .name(column)
                    .build();
            columnDtos.add(columnDto);
        }
        dto.setColumnDtos(columnDtos);
        //如果是外键,父表会查到相关信息,需要设置父表相关信息
        if("R".equals(constraintDto.getSrcType())){
            //如果是外键约束,需要查对应父表的信息
            List<String> referencedCon = query(getReferencedConsSql,new ColumnListHandler<>(),schema,dto.getTableName(),dto.getName());
            String referencedConsName = referencedCon.get(0);
            List<String> referencedTable = query(getReferencedTableSql,new ColumnListHandler<>(),schema, referencedConsName);
            String referencedTableName = referencedTable.get(0);
            List<String> referencedColumns = query(columnSql,new ColumnListHandler<>(),schema,referencedTableName,referencedConsName);
            dto.setReferencedColumns(referencedColumns.stream().collect(Collectors.joining(",")));
            dto.setReferencedTableName(referencedTableName);
            String fkConsSql = "select DELETE_RULE as deleteRule from dba_constraints where owner = ? and table_name = ? and constraint_name = ? ";
            ConstraintDto constraintDto1 = query(fkConsSql,new BeanHandler<>(ConstraintDto.class),schema,dto.getTableName(),dto.getName());
            if (constraintDto1 != null){
                dto.setDeleteRule(constraintDto1.getDeleteRule());
            }

            //相关列用ColumnDto形式存，传
            for (String column:referencedColumns) {
                TableDto.ColumnDto columnDto = TableDto.ColumnDto.builder()
                        .schema(schema)
                        .tableName(dto.getTableName())
                        .name(column)
                        .build();
                referencedColumnDto.add(columnDto);
            }
            dto.setReferencedColumnDtos(referencedColumnDto);
        }
        //约束涉及到的列,以逗号分隔成字符串
        dto.setColumns(columns.stream().collect(Collectors.joining(",")));
        //如果是check约束时,则condition不为空,需要取出并设置
        if(CollectionUtils.isNotEmpty(condition)){
            dto.setSearchCondition(condition.get(0));
        }

        dto.setConsType(srcConsTypeToConsType.get(dto.getSrcType()));
        return dto;
    }

    public List<List<String>> split(List<String> list,int size){
        if (list == null || list.size() == 0){
            return null;
        }

        int count = list.size();
        int pageCount = (count/size) + (count % size == 0 ? 0:1);
        List<List<String>> temp = Lists.newArrayList();

        for (int i = 0, from = 0, to = 0; i < pageCount; i++){
            from = i * size;
            to = from + size;
            to = to > count ? count : to;
            List<String> list1 = list.subList(from, to);
            temp.add(list1);
        }
        return temp;
    }
}
